---
author: hongbo
date: "2021-02-09"
previewImg: /blog/compiler_release_9_0.avif
title: ReScript 9.0
badge: release
description: |
  Featuring a new external stdlib configuration, some syntax improvements and a small breaking change for nested records.
---

## Introduction

We are happy to announce ReScript 9.0!

ReScript is a robustly typed language that compiles to efficient and human-readable JavaScript. It comes with one of the fastest build toolchains and offers first class support for interoperating with ReactJS and other existing JavaScript code.

Use `npm` to install the newest [9.0.1 release](https://www.npmjs.com/package/bs-platform/v/9.0.1) with the following command:

```sh
npm install bs-platform@9.0.1
```

You can also try our new release in the [Online Playground](/try).

In this post we will highlight the most notable changes. The full changelog for this release can be found [here](https://github.com/rescript-lang/rescript/blob/master/Changes.md#90).

## Compiler Improvements

### New External Stdlib Configuration

This is a long-awaited [feature request](https://github.com/rescript-lang/rescript/pull/2171).

Our compiler comes with a set of stdlib modules (such as `Belt`, `Pervasives`, etc.) for core functionality. Compiled ReScript code relies on the JS runtime version of these stdlib modules.

In previous versions, users couldn't ship their compiled JS code without defining a `package.json` dependency on `bs-platform`. Whenever a ReScript developer wanted to publish a package just for pure JS consumption / lean container deployment, they were required to use a bundler to bundle up their library / stdlib code, which made things way more complex and harder to understand.

To fix this problem, we introduced an `external-stdlib` configuration that allows specifying a pre-compiled stdlib npm package (`@rescript/std`). More details on how to use that feature can be found in our [External Stdlib](../docs/manual/build-external-stdlib.mdx) documentation.

### Less Bundle Bloat when Adding ReScript

With each release we keep a close eye on generating code that is optimized for tree-shaking. We also believe that we reached a milestone where ReScript reliably produces output that has almost no impact on our final JS bundle-sizes (this is what we call our "zero-cost" philosophy).

The bundled code is almost ReScript runtime free because our generated library code fits the tree-shaking principle really well. Tools like `esbuild` can easily drop unnecessary code and make sure that the final code stays lean.

We made a small [demo repo](https://github.com/bobzhang/zero-cost-rescript) and added the precompiled JS bundles to demonstrate what we've achieved. Check it out!

### Improved Code Generation for Pattern Matching

We fine-tuned our pattern matching engine to optimize the JS output even more. Here is an example of a pretty substantial optimization, based on [this issue](https://github.com/rescript-lang/rescript/issues/4924):

```res
type test =
  | NoArg
  | AnotherNoArg
  | OtherArg(int)

let test = x =>
  switch x {
  | NoArg => true
  | _ => false
  }
```

The snippet above will compile to the following JS output:

<CodeTab labels={["9.0 Output", "8.4 Output" ]}>

```js
function test(x) {
  return x === 0;
}
```

```js
function test(x) {
  if (typeof x === "number") {
    return x === 0;
  } else {
    return false;
  }
}
```

</CodeTab>

As you can see, the 9.0 compiler removes all the unnecessary `typeof` checks!

This is possible because our optimizer will try to analyze several predicates and get rid of redundant ones. More diffs can be found [here](https://github.com/rescript-lang/rescript/pull/4927/files?file-filters%5B%5D=.js).

Another important improvement is that we fixed the pattern match offset issue, which lead to the consequence that magic numbers will not be generated for complex pattern matches anymore.

For those interested in the details, here is a representative diff resulting from this cleanup:

```diff
function is_space(param){
- var switcher = param - 9 | 0;
- if (switcher > 4 || switcher < 0) {
-    return switcher == 23 ;
+ if (param > 13 || param < 9) {
+    return param === 32;
  } else {
-    return switcher !== 2;
+    return param != 11;
  }
}
```

## Syntax Improvements

### `when` -> `if`

Starting from 9.0, [`when` clauses](../docs/manual/pattern-matching-destructuring.mdx#when-clause) within a `switch` statement will automatically convert to the `if` keyword instead.

<CodeTab labels={["New (9.0)", "Old (8.4)"]}>

```res
switch person1 {
| Student({reportCard: {gpa}}) if gpa < 0.5 =>
  Js.log("What's happening")
| _ => () // do nothing
}
```

```res
switch person1 {
| Student({reportCard: {gpa}}) when gpa < 0.5 =>
  Js.log("What's happening")
| _ => () // do nothing
}
```

</CodeTab>

The `when` keyword is deprecated. The syntax will continue supporting it and the formatter will automatically convert to `if`, for a pain-free upgrade.

### Cleaner Polyvariant Syntax

Polyvariants with invalid identifier names (e.g. names including hypens `-`), don't require any special escaping syntax anymore:

<CodeTab labels={["New (9.0)", "Old (8.4)"]}>

```res
type animation = [ #"ease-in" | #"ease-out" ]
```

```res
type animation = [ #\"ease-in" | #\"ease-out" ]
```

</CodeTab>

We introduced this change to allow easier interop with existing JS string enums. In pure ReScript code, we'd still recommend our users to stick with valid identifier names instead (e.g. `easeIn` instead of `ease-in`).

## Breaking Changes

This release comes with a minor breaking change that shouldn't have much impact on the upgrade of existing codebases.

### Nested Records within Objects

Previously, if you wrote `{"user": {age: 10}}`, the inner record was interpreted as an object instead of a record (`{"user": {"age": 10}}`); this is a byproduct of some internal interop transformation details; with the ReScript syntax, this went from understandable to confusing, so we're changing it so that the inner record is indeed now treated as a record. This is an obvious fix, but a breaking change if you were accidentally leveraging that nested record as object.

Here is a code example before and after the change. Note how the `user` record secretly turns into a ReScipt object in the previous version:

<CodeTab labels={["9.0 Example", "8.4 Example" ]}>

```res
type user = {
  age: int
}

let data = {
  "user": {
    age: 1
  }
}

// This is the way: `age` should be usable via record accessor
let age = data["user"].age
```

```res
type user = {
  age: int
}

let data = {
  "user": {
    age: 1
  }
}

// This was the problem: The record implicitly turned
// into a ReScript object (which is confusing)
let age = data["user"]["age"]
```

</CodeTab>

More discussions on this change can be found [here](https://forum.rescript-lang.org/t/fixing-the-semantics-of-nested-objects-breaking-changes/976).

## Closing Note

We only highlighted a few user-facing features, but there are also some pretty interesting internal changes happening right now.

For example, we are tinkering with the idea on using WASM to replace Camlp4, and we are also working on a generalized visitor pattern that doesn't require objects.

We will discuss these topics in a separate development post, but we are already excited about the new possibilities this will bring within the compiler toolchain.

Happy Hacking!
